﻿// ===============================================================================
// ColumnSchema.cs
// EOS Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Contracts.Schema
{
    using System;

    /// <summary>
    /// Provides Schema Information about a column.
    /// </summary>
    [Serializable]
    public class ColumnSchema : IEquatable<ColumnSchema>
    {
        #region Properties

        /// <summary>
        /// Name of the column.
        /// </summary>
        private string _name;

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>Name of the column.</value>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Original SQL data type.
        /// </summary>
        private SqlType _sqlDataType;

        /// <summary>
        /// Gets or sets the type of the SQL data.
        /// </summary>
        /// <value>The type of the SQL data.</value>
        public SqlType SqlDataType
        {
            get { return _sqlDataType; }
            set { _sqlDataType = value; }
        }

        /// <summary>
        /// The data type how it is used in .NET.
        /// </summary>
        private Type _dataType;

        /// <summary>
        /// Gets or sets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        public Type DataType
        {
            get { return _dataType; }
            set { _dataType = value; }
        }

        /// <summary>
        /// Indicates if null is allowed for this column.
        /// </summary>
        private bool _allowNull;

        /// <summary>
        /// Gets or sets a value indicating whether null is allowed for this column.
        /// </summary>
        /// <value><c>true</c> if null is allowed; otherwise, <c>false</c>.</value>
        public bool AllowNull
        {
            get { return _allowNull; }
            set { _allowNull = value; }
        }

        /// <summary>
        /// Indicates, if this is an identity column.
        /// </summary>
        private bool _isIdentity;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is identity.
        /// </summary>
        /// <value><c>true</c> if this column is an identity; otherwise, <c>false</c>.</value>
        public bool IsIdentity
        {
            get { return _isIdentity; }
            set { _isIdentity = value; }
        }

        /// <summary>
        /// Indicats, if it is a primary key.
        /// </summary>
        private bool _isPrimaryKey;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is primary key.
        /// </summary>
        /// <value><c>true</c> if this column is a primary key; otherwise, <c>false</c>.</value>
        public bool IsPrimaryKey
        {
            get { return _isPrimaryKey; }
            set { _isPrimaryKey = value; }
        }

        /// <summary>
        /// Scale information for decimals.
        /// </summary>
        private int _scale;

        /// <summary>
        /// Gets or sets the scale.
        /// </summary>
        /// <value>The scale. Must be greater than 0.</value>
        public int Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }

        /// <summary>
        /// The length of the data.
        /// </summary>
        private int _length;

        /// <summary>
        /// Gets or sets the maximum length for the data in this column.
        /// </summary>
        /// <value>The length. Must be greater or equals than 0.</value>
        public int Length
        {
            get { return _length; }
            set { _length = value; }
        }

        /// <summary>
        /// Precision of decimals.
        /// </summary>
        private int _precision;

        /// <summary>
        /// Gets or sets the precision of decimals.
        /// </summary>
        /// <value>Precision of decimals. Must be greater or equals than 0.</value>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is 
        /// smaller than 0.</exception>
        public int Precision
        {
            get { return _precision; }
            set { _precision = value; }
        }

        /// <summary>
        /// The default value.
        /// </summary>
        private string _defaultValue;

        /// <summary>
        /// Gets or sets the default value.
        /// </summary>
        /// <value>The default value.</value>
        public string DefaultValue
        {
            get { return _defaultValue; }
            set { _defaultValue = value; }
        }

        /// <summary>
        /// Reference to the table, where the columns belongs to.
        /// </summary>
        private TableSchema _table;

        /// <summary>
        /// Gets or sets the <see cref="TableSchema"/> object, where the columns belongs to.
        /// </summary>
        /// <value>The table, where the columns belongs to.</value>
        public TableSchema Table
        {
            get { return _table; }
            set { _table = value; }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="ColumnSchema"/> class.
        /// </summary>
        public ColumnSchema()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ColumnSchema"/> class
        /// with the name of the column and the type.
        /// </summary>
        /// <param name="name">The name of the column.</param>
        /// <param name="dataType">Type of the data.</param>
        public ColumnSchema(string name, Type dataType)
        {
            this._name = name;
            this._dataType = dataType;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            return Equals(obj as ColumnSchema);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> 
        /// parameter; otherwise, false.
        /// </returns>
        public bool Equals(ColumnSchema other)
        {
            if (other == null)
            {
                return false;
            }

            bool result = true;

            if (!Object.Equals(_allowNull, other._allowNull))
            {
                result = false;
            }

            if (!Object.Equals(_dataType, other._dataType))
            {
                result = false;
            }

            if (!Object.Equals(_defaultValue, other._defaultValue))
            {
                result = false;
            }

            if (!Object.Equals(_isIdentity, other._isIdentity))
            {
                result = false;
            }

            if (!Object.Equals(_isPrimaryKey, other._isPrimaryKey))
            {
                result = false;
            }

            if (!Object.Equals(_length, other._length))
            {
                result = false;
            }

            if (!Object.Equals(_name, other._name))
            {
                result = false;
            }

            if (!Object.Equals(_precision, other._precision))
            {
                result = false;
            }

            if (!Object.Equals(_scale, other._scale))
            {
                result = false;
            }

            if (!Object.Equals(_sqlDataType, other._sqlDataType))
            {
                result = false;
            }

            return result;
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            int hashCode = 1;

            hashCode ^= _dataType.GetHashCode();
            hashCode ^= _length.GetHashCode();
            hashCode ^= _precision.GetHashCode();
            hashCode ^= _scale.GetHashCode();
            hashCode ^= _sqlDataType.GetHashCode();
            hashCode ^= _allowNull.GetHashCode();
            hashCode ^= _isIdentity.GetHashCode();
            hashCode ^= _isPrimaryKey.GetHashCode();

            if (_name != null)
            {
                hashCode ^= _name.GetHashCode();
            }

            if (_defaultValue != null)
            {
                hashCode ^= _defaultValue.GetHashCode();
            }

            return hashCode;
        }
    }
}
